#include <string.h>

/************************************************************************
int network_inferred (int N, int L, int pathway[L][N], int mode, int mode1, int mode2)

Input

N		number of nodes
L		number of steps
pathway		process
mode		0 : Process with steady state ; 1 : Cycle Process ; 2 : Triggled Cycle Process
mode1		0 : Budding Yeast Model ; 1 : Fission Yeast Model (with self-activation)
mode2		0 : Traditional SIM ; 1 : Restriction I ; 2 : Restriction II ; 3 : Restriciton I and II

Return Value	1 : Feasible
***********************************************************************/

void setup_simplified (int N, int L, int simplified[L][2 * N + 3], int n)
{
	int j, k;

	for (j = 0; j < L; j++)
	{
		if (simplified[j][2 * N] == 0)
			continue;
        
		simplified[j][2 * N + 1] = simplified[j][2 * N + 2] = 0;
		for (k = n; k < N; k++)
		{
			simplified[j][2 * N + 1] += simplified[j][k];
			simplified[j][2 * N + 2] += simplified[j][N + k];
		}
	}
}

void apply_Fr (int N, int L, int simplified[L][2 * N + 3], int Fr[N], int n)
{
	int j, k;

	/* Apply Forbidden R */
	for (k = 0; k < N; k++)
	{
		if (Fr[k] == 1)
		{
			for (j = 0; j < L; j++)
			{
				simplified[j][N + k] = 0;
			}
		}
	}

	/* Setup Array simplified */
	setup_simplified (N, L, simplified, n);
}

void simplify_Fr (int N, int L, int state[L][2 * N + 1], int simplified[L][2 * N + 3], int Fr[N], int i, int mode, int mode2)
{
	int j, k, flag;

	/* Get Forbidden R */
	for (j = 0; j < L; j++)
	{
		flag = 0; /* flag = 0 : additional requirement */

		if (j == 0 && mode == 2)
		{
			flag = 1;
		}
		else if (mode2 == 0)
		{
			flag = 1;
		}
		else if (mode2 == 1)
		{
			if (state[j][i] == state[j][N + i])
				flag = 1;
		}
		else if (mode2 == 2)
		{
			if (state[j][N + i] != state[j][2 * N])
				flag = 1;
		}
		else if (mode2 == 3)
		{
			if (state[j][i] == state[j][N + i] && state[j][N + i] != state[j][2 * N])
				flag = 1;
		}

		if (state[j][N + i] == 0)
		{
			simplified[j][2 * N] = -1;
			if (flag)
			{
				for (k = 0; k < N; k++)
				{
					if (state[j][k] == 1)
					{
						simplified[j][k] = 1;
						if (i != k)
							simplified[j][N + k] = 1;
					}
				}
			}
			else
			{
				for (k = 0; k < N; k++)
				{
					if (i == k)
					{
						if (state[j][k] == 1)
							simplified[j][k] = 1;
					}
					else
					{
						if (state[j][k] == 1 || state[j][N + k] == 1)
							simplified[j][k] = 1;
                                                
						if (state[j][k] == 1 && state[j][N + k] == 1)
							simplified[j][N + k] = 1;
					}
				}
			}
		}
		else
		{
			simplified[j][2 * N] = 1;
			if (flag)
			{
				for (k = 0; k < N; k++)
				{
					if (state[j][k] == 1)
					{
						simplified[j][k] = 1;
						if (i != k)
							Fr[k] = 1;
					}
				}
			}
			else
			{
				for (k = 0; k < N; k++)
				{
					if (i == k)
					{
						if (state[j][k] == 1)
							simplified[j][k] = 1;
					}
					else
					{
						if (state[j][k] == 1 && state[j][N + k] == 1)
							simplified[j][k] = 1;

						if (state[j][k] == 1 || state[j][N + k] == 1)
							Fr[k] = 1;
					}
				}
			}
		}
	}

	/* Apply Forbidden R and Reset Array simplified */
	apply_Fr (N, L, simplified, Fr, 0);
}

void apply_Fg (int N, int L, int simplified[L][2 * N + 3], int Rr[N], int Fg[N], int i, int n)
{
	int j, k;

	/* Apply Forbidden G */
	for (k = n; k < N; k++)
	{
		if ((i == k && Rr[k] == 1) || (i != k && Fg[k] == 1))
		{
			for (j = 0; j < L; j++)
			{
				if (simplified[j][k] == 1)
				{
					simplified[j][k] = 0;
					simplified[j][2 * N + 1] --;
					if (simplified[j][2 * N] == -1 && simplified[j][2 * N + 1] == 0)
					{
						simplified[j][2 * N] = 0;
					}
				}
			}
		}
	}
}
		
/* Return 1: find new member in Forbidden G */
int simplify_Fg (int N, int L, int simplified[L][2 * N + 3], int Rr[N], int Fg[N], int i, int n)
{
	int j, k, flag;

	flag = 0;

	/* Get Forbidden G */
	for (j = 0; j < L; j++)
	{
		if (simplified[j][2 * N] != -1 || simplified[j][2 * N + 2] != 0)
			continue;

		flag = 1;
		simplified[j][2 * N] = 0;
		for (k = n; k < N; k++)
		{
			if (simplified[j][k] == 1)
			{
				if (i == k)
					Rr[k] = 1;
				else
					Fg[k] = 1;
			}
		}
	}

	return flag;
}

int simplify (int N, int L, int simplified[L][2 * N + 3], int Rg[N], int Rr[N], int Fg[N], int Fr[N], int i)
{
	int j;

	if (simplify_Fg (N, L, simplified, Rr, Fg, i, 0) == 0)
			return 1;

	apply_Fg (N, L, simplified, Rr, Fg, i, 0);

	for (j = 0; j < L; j++)
	{
		if (simplified[j][2 * N] == 1 && simplified[j][2 * N + 1] == 0)
			return 0;
	}

	return 1;
}

int check_satisfy1 (int N, int L, int state[L][2 * N + 1], int i, int mode, int mode2)
{
	int Rg[N], Rr[N]; /* Rg/Rr: Required G/R */
	int Fg[N], Fr[N]; /* Fg/Fr: Forbidden G/R */
	int simplified[L][2 * N + 3]; /* [0, N):g; [N, 2*N):r; 2*N:-1/0/1; 2*N+1/2:sum of g/r */
	int j, k;

	/* Set the initial value */
	for (j = 0; j < N; j++)
	{
		Rg[j] = Rr[j] = Fg[j] = Fr[j] = 0;
	}
	Fg[i] = 1; /* No self-activation */

	for (j = 0; j < L; j++)
	{
		for (k = 0; k < 2 * N + 3; k++)
		{
			simplified[j][k] = 0;
		}
	}

	simplify_Fr (N, L, state, simplified, Fr, i, mode, mode2);

	for (j = 0; j < L; j++)
	{
		if (simplified[j][2 * N] == 1 && simplified[j][2 * N + 1] == 0)
			return 0;
	}

	return simplify (N, L, simplified, Rg, Rr, Fg, Fr, i);
}

int check_satisfy2 (int N, int L, int pathway[L][N], int i, int mode, int mode1, int mode2)
{
	int j, k;
	int state[L - 1][2 * (N + mode1) + 1];

	for (j = 0; j < L - 1; j++)
	{
		state[j][0] = state[j][N + mode1] = 1;
		for (k = 0; k < N; k++)
		{
			state[j][k + mode1] = pathway[j][k];
			state[j][k + N + 2 * mode1] = pathway[j + 1][k];
		}
        
		if (j != L - 2)
			state[j][2 * (N + mode1)] = pathway[j + 2][i];
		else
		{
			if (mode == 1)
				state[j][2 * (N + mode1)] = pathway[1][i];
			else
				state[j][2 * (N + mode1)] = pathway[j + 1][i];
		}
	}
        
	return check_satisfy1 (N + mode1, L - 1, state, i + mode1, mode, mode2);
}

int network_inferred (int N, int L, int pathway[L][N], int mode, int mode1, int mode2)
{
	int i;

	for (i = 0; i < N; i++)
	{
		if (mode == 2 && i == 0) /* Don't need to check the triggle node */
			continue;

		if (check_satisfy2 (N, L, pathway, i, mode, mode1, mode2) == 0)
			return 0;
	}

	return 1;
}
